package com.gaj.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * Mapper动态调用工具类
 *
 * 职责：
 * 1. 动态获取Mapper接口对象
 * 2. 执行数据库插入操作
 * 3. 管理表主键ID缓存
 * 4. 处理实体类与Mapper的映射关系
 *
 * 使用场景：
 * 在XML解析过程中，根据实体类名称动态调用对应的Mapper进行数据库操作
 *
 * @author 系统
 * @version 1.0
 */
public class MapperUtils {

    // ==================== 静态变量 ====================

    /**
     * 存储各个表的主键ID值
     * Key格式: 表名+id (如: "userid", "orderid")
     * Value: 主键ID值
     */
    public static HashMap<String, Integer> tableIds = new HashMap<>();

    // ==================== Mapper获取方法 ====================

    /**
     * 根据实体类全限定名获取对应的Mapper接口对象
     *
     * 获取流程：
     * 1. 从实体类全限定名中提取简单类名
     * 2. 构造Mapper的bean名称（驼峰命名）
     * 3. 检查Mapper的bean是否存在
     * 4. 获取Mapper的bean对象
     *
     * @param entityClassName 实体类全限定名（如：com.gaj.entity.User）
     * @return Mapper接口对象，如果不存在返回null
     */
    public static Object getMapperBeanByEntityName(String entityClassName) {
        try {
            // 1. 根据实体类全限定名称获取简单类名
            String simpleClassName = extractSimpleClassName(entityClassName);
            //System.out.println("        提取简单类名: " + simpleClassName);

            // 2. 构造Mapper的bean名称（驼峰命名）
            String mapperBeanName = toLowerCamelCase(simpleClassName + "Mapper");
            //System.out.println("        构造Mapper Bean名称: " + mapperBeanName);

            // 3. 检查Mapper的bean是否存在
            if (!SpringBeanUtils.containsBean(mapperBeanName)) {
                System.out.println("        未找到Mapper: " + mapperBeanName + "，跳过数据库操作");
                return null;
            }

            // 4. 获取Mapper的bean对象
            Object mapperBean = SpringBeanUtils.getBean(mapperBeanName);
            //System.out.println("        成功获取Mapper Bean: " + mapperBeanName);
            return mapperBean;

        } catch (Exception e) {
            System.err.println("        获取Mapper Bean失败: " + entityClassName + ", 错误: " + e.getMessage());
            throw new RuntimeException("获取Mapper Bean失败", e);
        }
    }

    /**
     * 根据实体类全限定名获取对应的Mapper接口Class对象
     *
     * 获取流程：
     * 1. 获取Mapper的bean对象
     * 2. 获取Mapper的Class对象
     * 3. 处理JDK动态代理情况，获取原始接口
     *
     * @param entityClassName 实体类全限定名
     * @return Mapper接口Class对象，如果不存在返回null
     */
    public static Class<?> getMapperClassByEntityName(String entityClassName) {
        // 1. 根据实体类全限定名称获取Mapper的bean对象
        Object mapperObject = getMapperBeanByEntityName(entityClassName);
        if (mapperObject == null) {
            return null;
        }

        // 2. 获取Mapper的Class对象（即接口Class）
        Class<?> mapperClass = mapperObject.getClass();

        // 3. 处理JDK动态代理情况，获取原始接口
        // 注意：MyBatis Mapper通常是JDK代理，需要获取原始接口
        Class<?>[] interfaces = mapperClass.getInterfaces();
        if (interfaces.length > 0) {
            mapperClass = interfaces[0]; // 取第一个接口，通常就是mapper接口
            //System.out.println("        获取Mapper接口Class: " + mapperClass.getName());
        }

        return mapperClass;
    }

    // ==================== 工具方法 ====================

    /**
     * 从实体类全限定名中提取简单类名
     *
     * 提取逻辑：
     * 1. 检查输入是否为空
     * 2. 查找最后一个"."的位置
     * 3. 截取"."后面的部分作为简单类名
     *
     * @param entityClassName 实体类全限定名（如：com.gaj.entity.User）
     * @return 简单类名（如：User）
     */
    public static String extractSimpleClassName(String entityClassName) {
        if (entityClassName == null || entityClassName.trim().isEmpty()) {
            return "";
        }

        int lastDotIndex = entityClassName.lastIndexOf(".");
        if (lastDotIndex != -1) {
            return entityClassName.substring(lastDotIndex + 1);
        }

        return entityClassName;
    }

    /**
     * 将Mapper的beanName转换为小写驼峰命名法
     *
     * 转换逻辑：
     * 1. 检查输入是否为空
     * 2. 将首字母转换为小写
     * 3. 保持其余字母不变
     *
     * @param originalMapperBeanName 原始Mapper的beanName（如：UserMapper）
     * @return 转换后的小写驼峰命名法（如：userMapper）
     */
    public static String toLowerCamelCase(String originalMapperBeanName) {
        if (originalMapperBeanName == null || originalMapperBeanName.isEmpty()) {
            return originalMapperBeanName;
        }

        return originalMapperBeanName.substring(0, 1).toLowerCase() + originalMapperBeanName.substring(1);
    }


    // ==================== 数据库操作方法 ====================

    /**
     * 执行选择性插入操作
     *
     * 执行流程：
     * 1. 获取Mapper的Class对象
     * 2. 获取Mapper的bean对象
     * 3. 通过反射获取insertSelective方法
     * 4. 执行插入操作
     * 5. 更新表主键ID缓存
     *
     * @param entityClassName 实体类全限定名
     * @param instance 实体类实例
     * @return 影响的行数，失败返回-1
     */
    public static int insertSelective(String entityClassName, Object instance) {
        //System.out.println("        开始执行选择性插入操作: " + entityClassName);

        // 1. 根据实体类全限定名称获取Mapper的Class对象
        Class<?> mapperClass = getMapperClassByEntityName(entityClassName);
        if (mapperClass == null) {
            System.out.println("        无法获取Mapper Class，插入操作失败");
            return -1;
        }

        // 2. 根据Mapper的Class对象获取Mapper的bean对象
        Object mapperObject = getMapperBeanByEntityName(entityClassName);
        if (mapperObject == null) {
            System.out.println("        无法获取Mapper Bean，插入操作失败");
            return -1;
        }

        try {
            // 3. 通过反射获取insertSelective方法
            Method insertSelective = mapperClass.getDeclaredMethod("insertSelective", instance.getClass());
            //System.out.println("        获取insertSelective方法成功");

            // 4. 反射执行insertSelective方法
            int result = (int) insertSelective.invoke(mapperObject, instance);

            if (result > 0) {
                // 5. 更新表主键ID缓存
                putTableId(instance);
                System.out.println("        反射调用 " + mapperClass.getName() + ".insertSelective() 成功, 影响行数: " + result);
            } else {
                System.out.println("        反射调用 " + mapperClass.getName() + ".insertSelective() 失败, 影响行数: " + result);
            }

            return result;

        } catch (Exception e) {
            System.err.println("        反射调用 " + mapperClass.getName() + ".insertSelective() 失败, 异常信息: " + e.getMessage());
            e.printStackTrace();
            return -1;
        }
    }

    // ==================== 主键ID缓存管理 ====================

    /**
     * 更新表主键ID缓存
     *
     * 更新逻辑：
     * 1. 获取实体类的Class对象
     * 2. 通过反射获取id字段
     * 3. 将主键ID值存储到缓存中
     *
     * 缓存Key格式：表名+id（如：userid, orderid）
     *
     * @param instance 实体类实例
     */
    private static void putTableId(Object instance) {
        try {
            Class<?> clazz = instance.getClass();
            String tableName = clazz.getSimpleName().toLowerCase();

            // 通过反射获取id字段
            Field id = clazz.getDeclaredField("id");
            id.setAccessible(true);

            // 获取主键ID值
            Object idValue = id.get(instance);
            Integer idInt = Integer.valueOf(String.valueOf(idValue));

            // 存储到缓存中
            String cacheKey = tableName + "id";
            tableIds.put(cacheKey, idInt);

            //System.out.println("        更新主键ID缓存: " + cacheKey + " = " + idInt);

        } catch (NoSuchFieldException | IllegalAccessException e) {
            System.err.println("        更新主键ID缓存失败: " + e.getMessage());
            throw new RuntimeException("更新主键ID缓存失败", e);
        }
    }
}
